﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace Aeon.Configuration
{
    /// <summary>
    /// Manages drive and device configurations.
    /// </summary>
    public sealed class ConfigurationMapper
    {
        #region Private Fields
        /// <summary>
        /// Contains all of the registered drive configurations.
        /// </summary>
        private readonly List<DriveConfigurationInfo> driveConfigs = new List<DriveConfigurationInfo>();
        /// <summary>
        /// Contains all of the registered device configurations.
        /// </summary>
        private readonly List<DeviceConfigurationInfo> deviceConfigs = new List<DeviceConfigurationInfo>();
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the ConfigurationMapper class.
        /// </summary>
        public ConfigurationMapper()
        {
        }
        #endregion

        #region Public Properties
        /// <summary>
        /// Gets the collection of registered drive configurations.
        /// </summary>
        public ReadOnlyCollection<DriveConfigurationInfo> DriveConfigurations
        {
            get { return this.driveConfigs.AsReadOnly(); }
        }
        /// <summary>
        /// Gets the collection of registered device configurations.
        /// </summary>
        public ReadOnlyCollection<DeviceConfigurationInfo> DeviceConfigurations
        {
            get { return this.deviceConfigs.AsReadOnly(); }
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Registers a new drive configuration type.
        /// </summary>
        /// <param name="configurationType">Drive configuration type to register.</param>
        public void RegisterDriveConfiguration(Type configurationType)
        {
            if(configurationType == null)
                throw new ArgumentNullException("configurationType");
            if(!typeof(IDriveConfiguration).IsAssignableFrom(configurationType))
                throw new ArgumentException("The type does not implement IDriveConfiguration.");

            var config = this.driveConfigs.Find(c => c.ConfigType == configurationType);
            if(config == null)
                this.driveConfigs.Add(new DriveConfigurationInfo(configurationType));
        }
        /// <summary>
        /// Registers a new drive configuration view.
        /// </summary>
        /// <param name="viewType">Type of drive configuration view to register.</param>
        public void RegisterDriveConfigurationView(Type viewType)
        {
            if(viewType == null)
                throw new ArgumentNullException("viewType");
            if(!typeof(IDriveConfigurationView).IsAssignableFrom(viewType))
                throw new ArgumentException("The type does not implement IDriveConfigurationView.");

            var attributes = viewType.GetCustomAttributes(typeof(DriveConfigurationViewAttribute), false);
            if(attributes == null || attributes.Length == 0)
                throw new ArgumentException("The type does not have a DriveConfigurationViewAttribute.");

            foreach(DriveConfigurationViewAttribute viewAttribute in attributes)
            {
                var configType = viewAttribute.ConfigurationType;
                if(configType == null)
                    throw new ArgumentException("The specified view type could not be resolved.");

                var config = this.driveConfigs.Find(c => c.ConfigType == configType);
                if(config == null)
                    throw new ArgumentException("The required drive configuration type has not been registered.");

                config.ViewType = viewType;
            }
        }
        /// <summary>
        /// Registers a new device configuration type.
        /// </summary>
        /// <param name="configurationType">Device configuration type to register.</param>
        public void RegisterDeviceConfiguration(Type configurationType)
        {
            if(configurationType == null)
                throw new ArgumentNullException("configurationType");
            if(!typeof(IDeviceConfiguration).IsAssignableFrom(configurationType))
                throw new ArgumentException("The type does not implement IDeviceConfiguration.");

            var config = this.deviceConfigs.Find(c => c.ConfigType == configurationType);
            if(config == null)
                this.deviceConfigs.Add(new DeviceConfigurationInfo(configurationType));
        }
        /// <summary>
        /// Returns a new instance of the default configuration.
        /// </summary>
        /// <returns>New instance of the default configuration.</returns>
        public VirtualMachineConfiguration CreateDefaultConfiguration()
        {
            var config = new VirtualMachineConfiguration();
            foreach(var device in this.DeviceConfigurations)
                config.Devices.Add(device.Create());

            return config;
        }
        #endregion
    }
}
